#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <libgen.h>
#include <dirent.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include "config.h"

#define CONSTRUCT_CFG(a, b) \
    do { \
        if (sizeof(a) > sizeof(b)) { \
            LOG_ERR("sizeof %s is larger than %d\n", (a), sizeof(b)); \
            return 1; \
        } \
        snprintf((b), sizeof(b), "%s", (a)); \
    } while(0)

global_config g_cfg = {0};

static char* sensor_str(int sensor_id)
{
    switch (sensor_id) {
        case SENSOR_COCTS:
            return SENSOR_COCTS_STR;
        case SENSOR_CZI:
            return SENSOR_CZI_STR;
        case SENSOR_UVI:
            return SENSOR_UVI_STR;
        default:
            return NULL;
    }
}

static char* DPG_method_str(int DPG_method)
{
    switch (DPG_method) {
        case DPG_TRBMCC:
            return "TRBMCC";
        case DPG_TRBMC:
            return "TRBMC";
        case DPG_LWRBMC:
            return "LWRBMC";
        case DPG_LWRBMCC:
            return "LWRBMCC";
        case DPG_RAYBMC:
            return "RAYBMC";
        default:
            return NULL;
    }
}

static int sensor_str2id(const char * sensor_str)
{
    if (!strcmp(sensor_str, SENSOR_COCTS_STR)) {
        return SENSOR_COCTS;
    } else if (!strcmp(sensor_str, SENSOR_UVI_STR)) {
        return SENSOR_UVI;
    } else if (!strcmp(sensor_str, SENSOR_CZI_STR)) {
        return SENSOR_CZI;
    } else {
        LOG_ERR("invalid sensor %s\n", sensor_str);
        return -1;
    }
}

static int DPG_method_str2id(const char * DPG_method_str)
{
    if (!strcmp(DPG_method_str, "TRBMCC")) {
        return DPG_TRBMCC;
    } else if (!strcmp(DPG_method_str, "TRBMC")) {
        return DPG_TRBMC;
    } else if (!strcmp(DPG_method_str, "LWRBMC")) {
        return DPG_LWRBMC;
    } else if (!strcmp(DPG_method_str, "LWRBMCC")) {
        return DPG_TRBMCC;
    } else if (!strcmp(DPG_method_str, "RAYBMC")) {
        return DPG_TRBMCC;
    } else {
        LOG_ERR("invalid method %s\n", DPG_method_str);
        return -1;
    }
}

static void usage(int argc, char *argv[])
{
    char * p = strdup(argv[0]);
    fprintf(stderr, "Usage: %s [OPTION...]\n"
            "       -i    HY1C L2A h5 file path\n"
            "       -r    MODIS L2A h5 file path\n"
            "       -c    config path [OPTIONAL], if not provisioned,\n"
            "             will search it in current directory.\n"
            "       -o    output file path [OPTIONAL], if not provisioned,\n"
            "             will output into the same directory as -i option\n"
            "       -s    SENSOR ID select from COCTS/UVI/CZI\n"
            "       -m    DPG method select from TRBMCC/TRBMC/LWRBMC/LWRBMCC/RAYBMC\n"
            "       \n"
            "example: coeff_calc -i /home/test/HY1C_L2A.h5\n"
            "                    -r /home/test/MODIS_L2A.h5\n"
            "                    -c /home/test/HY1C/config\n"
            "                    -o /home/test/OUTPUT.cal\n"
            "                    -s COCTS\n"
            "                    -m TRMBCC\n"
            ,basename(argv[0]));
    free(p);
    exit(1);
}

static int parse_config_file(void* config, const char* section,
        const char* name, const char* value)
{
    global_config *cfg = (global_config *)config;

#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0
    if (MATCH("GLOBAL", "log_level")) {
        if (strcmp(value, "DEBUG") == 0)
            cfg->log_level = LogLevel_Debug;
        else if (strcmp(value, "INFO") == 0)
            cfg->log_level = LogLevel_Info;
        else if (strcmp(value, "WARN") == 0)
            cfg->log_level = LogLevel_Warn;
        else if (strcmp(value, "ERROR") == 0)
            cfg->log_level = LogLevel_Error;
        else
            printf("invalid log level %s\n", value);
    }

    if (MATCH("GLOBAL", "log_file")) {
        if (strcmp(value, "stderr") == 0) {
            cfg->pLog = stderr;
        } else if (strcmp(value, "stdout") == 0) {
            cfg->pLog = stdout;
        } else if ((cfg->pLog = fopen(value, "a")) == NULL) {
            fprintf(stderr, "Init log failed.\n");
        }
    }

    if (MATCH("GLOBAL", "mode")) {
        cfg->mode = atoi(value);
    }

    if (MATCH("GLOBAL", "model_number_base")) {
        cfg->model_number_base = atoi(value);
    }
#if 0
    if (MATCH("GLOBAL", "aerosol_LUT")) {
        strncpy(g_cfg.aerosol_LUT, value, strlen(value));
    }
#endif


    if (MATCH(cfg->sensor, "trbmc_region")) {
        cfg->trbmc_region = atoi(value);
    }

    if (MATCH(cfg->DPG_method_str, "filter_base_type")) {
        cfg->filter_base_type = atoi(value);
    }

    if (MATCH(cfg->sensor, "calc_again")) {
        cfg->again.calc_again = atoi(value);
    }

    if (MATCH(cfg->sensor, "keep_original_gain")) {
        cfg->again.keep_original_gain = atoi(value);
    }

    if (MATCH(cfg->sensor, "gain")) {
        sscanf(value, "%f,%f", &(cfg->again.gain[0]), &(cfg->again.gain[1]));
    }

    return 1;
}

int init_cfg(int argc, char* argv[])
{
    if (argc < 9) {
        usage(argc, argv);
    }

    int ch;
    while ((ch = getopt(argc, argv, "i:r:c:o:s:m:a:")) != -1) {
        switch(ch) {
            case 'i':
                CONSTRUCT_CFG(optarg, g_cfg.hy1c_f);
                break;
            case 'r':
                CONSTRUCT_CFG(optarg, g_cfg.modis_f);
                break;
            case 'c':
                CONSTRUCT_CFG(optarg, g_cfg.config_f);
                break;
            case 'o':
                CONSTRUCT_CFG(optarg, g_cfg.out_gain);
                break;
            case 's':
                CONSTRUCT_CFG(optarg, g_cfg.sensor);
                break;
            case 'm':
                CONSTRUCT_CFG(optarg, g_cfg.DPG_method_str);
                break;
            default:
                LOG_ERR("invalid argument");
                return 1;
        }
    }

    g_cfg.sensor_id = sensor_str2id(g_cfg.sensor);
    g_cfg.DPG_method = DPG_method_str2id(g_cfg.DPG_method_str);

    g_cfg.again.gain[0] = BAD_FLT;
    g_cfg.again.gain[1] = BAD_FLT;

    int ret = 0;

    const char *coeff_calc_homepath = getenv("COEFF_CALC_HOMEPATH");
    char config_path[PATH_MAXSIZE] = {0};
    sprintf(config_path, "%s/%s", coeff_calc_homepath, "coeff_calc.cfg");

    char abs_path_buff[PATH_MAX];

    realpath(config_path, abs_path_buff);
    LOG_INFO("Loading config from %s\n", abs_path_buff);

    if ((ret = ini_parse(config_path, parse_config_file, &g_cfg)) < 0) {
        printf("Can't load configurtion. ret = %d\n", ret);
        //return 1;
    }


    float modis_wave[] = {412.0, 443, 488, 531, 555, 667, 748, 869}; // MODIS
    memcpy(g_cfg.wave_modis, modis_wave, sizeof(modis_wave));
    g_cfg.modis_nwave = sizeof(modis_wave) / sizeof(modis_wave[0]);

    float *p = NULL;
    size_t np = 0;

    if (g_cfg.sensor_id == SENSOR_COCTS) { // HY1C-COCTS
        float hy1c_wave[] = {412.0, 443, 490, 520, 565, 670, 750, 865};
        p = hy1c_wave;
        np = sizeof(hy1c_wave) / sizeof(hy1c_wave[0]);
    } else if (g_cfg.sensor_id == SENSOR_UVI) { // HY1C-UVI
        float hy1c_wave[] = {354.0, 356, 384, 386};
        p = hy1c_wave;
        np = sizeof(hy1c_wave) / sizeof(hy1c_wave[0]);
    } else { // HY1C-CZI
        float hy1c_wave[] = {460.0, 560, 650, 825};
        p = hy1c_wave;
        np = sizeof(hy1c_wave) / sizeof(hy1c_wave[0]);
    }

    memcpy(g_cfg.wave_hy1c, p, sizeof(float) * np);
    g_cfg.hy1c_nwave = (int)np;

    if ((strlen(g_cfg.hy1c_f) <= 0) || (strlen(g_cfg.modis_f) <= 0)) {
        LOG_ERR("Must gave the file name.");
        return 1;
    }

    if (strlen(g_cfg.config_f) <= 0) {
        char *current_path = get_current_dir_name();
        sprintf(g_cfg.config_f, "%s/%s", current_path, "config");
    }

    if (strlen(g_cfg.out_gain) <= 0) {
        char *p = strdup(g_cfg.hy1c_f);
        sprintf(g_cfg.out_gain, "%s", dirname(p));
        free(p);
    }

    if (g_cfg.sensor_id == SENSOR_CZI)
        sprintf(g_cfg.config_f, "%s%s", g_cfg.config_f, "/HY1C-CZI");
    else if (g_cfg.sensor_id == SENSOR_UVI)
        sprintf(g_cfg.config_f, "%s%s", g_cfg.config_f, "/HY1C-UVI");
    else
        sprintf(g_cfg.config_f, "%s%s", g_cfg.config_f, "/HY1C");

#if 1 // original get the aermodules from directory, now get from input
    DIR *dir;
    struct dirent *ptr;

    char aerosol_path[PATH_MAXSIZE] = {0};

    sprintf(aerosol_path, "%s%s", g_cfg.config_f, "/aerosol");

    if ((dir=opendir(aerosol_path)) == NULL) {
        LOG_ERR("Open %s error ... %s", aerosol_path, strerror(errno));
        return 1;
    }

    while ((ptr = readdir(dir)) != NULL) {
        if(strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0)
            continue;
        else if(ptr->d_type == 8) {   ///file
            if (strcmp(strrchr(ptr->d_name,'.'), ".hdf"))
                continue;

            if (strstr(ptr->d_name, "default") != NULL)
                continue;

            char *p = ptr->d_name;
            char *first_underline_pos = strchr(p, '_');
            char *second_underline_pos = strchr(first_underline_pos + 1, '_');
            char *dot_pos = strchr(second_underline_pos + 1, '.');

            static int first = 1;
            if (first) {
                strncpy(g_cfg.aerosol_LUT, first_underline_pos + 1, second_underline_pos - first_underline_pos - 1);
                first = 0;
            }

            snprintf(g_cfg.aermodules[g_cfg.naermodules++],
                    dot_pos - second_underline_pos,
                    "%s", second_underline_pos + 1);
        }
    }

    int order_models(const void *p1, const void *p2)
    {
        char *x = (char*)p1;
        char *y = (char*)p2;

        int xrh = (x[1] - '0') * 10 + x[2] - '0';
        int yrh = (y[1] - '0') * 10 + y[2] - '0';

        int xsd = (x[4] - '0') * 10 + x[5] - '0';
        int ysd = (y[4] - '0') * 10 + y[5] - '0';

        if (xrh == yrh) {
            if (xsd > ysd)
                return( -1);
            else
                return(1);
        } else {
            if (xrh > yrh)
                return( 1);
            else
                return(-1);
        }
    }

    qsort(g_cfg.aermodules, g_cfg.naermodules, sizeof(char) * 32, (int (*)(const void *,const void *)) order_models);

#if 0
    for(int i = 0; i < g_cfg.naermodules; i++)
        printf("%s\n", g_cfg.aermodules[i]);
#endif

    closedir(dir);
#endif

    msl12_input_init(&(g_cfg.input));

    /* Parse input parameters */
    if (msl12_input(NULL, 0, "l2gen", &(g_cfg.input), NULL) != 0) {
        HY1C_out("-E- %s: Error parsing input parameters.\n",argv[0]);
        exit(FATAL_ERROR);
    }

    g_cfg.input.mode = g_cfg.mode;
    HY1C_out("mode = %d\n", g_cfg.input.mode);

#if 0
    memcpy(g_cfg.aermodules, g_cfg.input.aermodels, sizeof(g_cfg.input.aermodels));
    g_cfg.naermodules = g_cfg.input.naermodels;
#endif

    return 0;
}
